import asyncio
import json
import hashlib
import urllib.parse
import logging

from .QAsyncNetworkManager import QAsyncNetworkManager
from PyQt6 import QtCore
from PyQt6.QtCore import pyqtProperty, pyqtSignal

MY_ROLES_PATH = "/logon/myRoles"
LOGON_LOGIN_VALIDATE = "/logon/loginValidate"
MY_APPS = "/logon/myApps"
INVOKE_RPC = "/*.jsonRequest"


class QAsyncSsdevClient(QtCore.QObject):
    receive_error = pyqtSignal(str)

    def __init__(self, bbp_base_url: str, loop=None, parent: 'QtCore.QObject' = None,
                 timeout: int = 5):
        super(QAsyncSsdevClient, self).__init__(parent)
        self.bbp_base_url = bbp_base_url if not bbp_base_url.endswith('/') else bbp_base_url[:-1]
        self.charset = 'UTF-8'
        self.timeout = timeout
        loop = loop if loop is not None else asyncio.get_event_loop()
        self.network_manager = QAsyncNetworkManager(parent=self, loop=loop)
        self.token = None
        self.timeout = timeout

    def _parse_http_response(self, rsp_bytes: bytes):
        s = rsp_bytes.decode(self.charset)
        logging.info("receive rsp:%s", s)
        rsp_body = json.loads(s)
        code = rsp_body['code']
        if code == 200:
            if 'body' in rsp_body:
                return rsp_body['body']
        else:
            if 'msg' in rsp_body:
                msg = rsp_body['msg']
                self.receive_error.emit(msg)

        return None

    def _convert_obj_to_bytes(self, obj):
        if obj is None:
            return None
        elif isinstance(obj, QtCore.QByteArray):
            return obj
        elif isinstance(obj, bytes):
            return QtCore.QByteArray(obj)
        elif isinstance(obj, str):
            return self._convert_obj_to_bytes(obj.encode(self.charset))
        else:
            s = json.dumps(obj)
            s_bytes = s.encode(self.charset)
            return QtCore.QByteArray(s_bytes)

    async def _http_get(self, url: str):
        rsp = await self.network_manager.http_request(url, 'GET', timeout=self.timeout)
        return self._parse_http_response(rsp)

    async def _json_post(self, url: str, body, headers: dict = {}):
        request_headers = {'Encoding': 'UTF-8', 'Content-Type': 'application/json'}
        for k, v in headers.items():
            request_headers[k] = v

        body_bytes = self._convert_obj_to_bytes(body)

        rsp = await self.network_manager.http_request(url=url, method='POST', headers=request_headers,
                                                      body_data=body_bytes, timeout=self.timeout)

        return self._parse_http_response(rsp)

    async def my_roles(self, tenant_id: str, login_name: str, password: str):
        md5 = hashlib.md5()
        md5.update(password.encode(self.charset))
        pwd_md5 = md5.hexdigest()
        req = {'loginName': login_name,
               'pwd': pwd_md5,
               'tenantId': tenant_id,
               'forAccessToken': True}
        url = self.bbp_base_url + MY_ROLES_PATH
        rsp = await self._json_post(url, req)
        return rsp

    async def fetch_token(self, tenant_id: str, login_name: str, role: dict):
        url = self.bbp_base_url + LOGON_LOGIN_VALIDATE
        req = {'loginName': login_name,
               'tenantId': tenant_id
               }
        login_validate_rsp = await self._json_post(url, req)

        token_url = self.bbp_base_url + MY_APPS + '?'
        urt = role['id']
        ud_id = role['ud_id']
        urt_dept = ud_id if ud_id != urt else ''
        params = {'urt': urt,
                  'deep': '3',
                  'platform': '256',
                  'urtDept': urt_dept
                  }
        token_url = token_url + urllib.parse.urlencode(params)

        my_apps_rsp = await self._http_get(token_url)

        cookie_jar = self.network_manager.cookieJar()

        all_cookies = cookie_jar.allCookies()

        for c in all_cookies:
            if 'tk' == c.name():
                return c.value()

        return None

    async def invoke_rpc(self, service_id: str, method: str, *args):
        url = self.bbp_base_url + INVOKE_RPC

        headers = {'X-Service-Id': service_id,
                   'X-Service-Method': method}

        rsp = await self._json_post(url, json.dumps(args), headers=headers)
        return rsp
